;
(function(window, $) {

    var DRAW_BOARD_ENABLE = true

    var getMouseOffset;
    (function() {
        var elementOffset = function(elem) {
            var x, y;
            x = 0;
            y = 0;
            while (true) {
                x += elem.offsetLeft;
                y += elem.offsetTop;
                if (!(elem = elem.offsetParent)) {
                    break;
                }
            }
            return {
                x: x,
                y: y
            };
        };

        //event是jquery中的event对象 原生event有兼容问题
        getMouseOffset = function(elem, event) {
            var offset = elementOffset(elem);
            return {
                left: event.pageX - offset.x,
                top: event.pageY - offset.y
            };
        };
    })();

    var drag = function(selector, option) {

        var $els = $(selector),
            dragging = option.dragging || $.noop,
            dragend = option.dragend || $.noop,
            dragcontinue = option.dragcontinue || $.noop,
            dragpause = option.dragpause || $.noop,
            dragstart = option.dragstart || $.noop;

        $els.each(function() {

            var mouse = $.proxy(function(e) {
                return getMouseOffset(this, e);
            }, this);

            var mousestate = {
                down: false
            };

            var $this = $(this);
            $this.mousedown(function(e) {
                // break if enable is false
                if (!DRAW_BOARD_ENABLE) return

                if (mousestate.down == true) {
                    return false;
                } else {
                    mousestate.down = true;
                    mousestate.downOffset = mouse(e);
                    dragstart.call(this, mousestate.downOffset);
                }
            });

            $this.mouseup(function(e) {
                mousestate.down = false;
                if (mousestate.move == true) {
                    dragend.call(this, mousestate.downOffset, mouse(e));
                    mousestate.move = false;
                }
            });

            $this.mouseout(function(e) {
                if (!mousestate.down) return;
                dragpause.call(this, mouse(e));
                mousestate.out = true;
            });

            var move = function(e) {
                if (!mousestate.down) return;
                if (mousestate.out === true) {
                    //如果鼠标在离开绘图区域后弹起，再次进入绘图区域，不进行重新计算，而是继续之前的绘图
                    dragcontinue.call(this, mouse(e));
                }
                mousestate.out = false;
                mousestate.move = true;
                dragging.call(this, mousestate.downOffset, mouse(e));
            }
            $this.mousemove(move);
        });
    };
    var util = {};
    (function() {
        //两点间的距离
        util.len = function(start, end) {
            var w = end.left - start.left
            var h = end.top - start.top;
            return Math.sqrt(Math.pow(w, 2) + Math.pow(h, 2));
        };
        util.throttle = function(func, wait, options) {
            var context, args, result;
            var timeout = null;
            var previous = 0;
            options || (options = {});
            var later = function() {
                previous = options.leading === false ? 0 : _.now();
                timeout = null;
                result = func.apply(context, args);
                context = args = null;
            };
            return function() {
                var now = util.now();
                if (!previous && options.leading === false) previous = now;
                var remaining = wait - (now - previous);
                context = this;
                args = arguments;
                if (remaining <= 0) {
                    clearTimeout(timeout);
                    timeout = null;
                    previous = now;
                    result = func.apply(context, args);
                    context = args = null;
                } else if (!timeout && options.trailing !== false) {
                    timeout = setTimeout(later, remaining);
                }
                return result;
            };
        };
        
        util.now = Date.now || function() { return new Date().getTime(); };

        util.pxValue=function(px){
            var r = /(-?\d+)px/
            var rs = r.exec(px);
            if(rs){
                return rs[1];
            }
        }

        util.inRect=function(pos, rect){
            return      pos.left > rect.left
                    && pos.top > rect.top
                    && pos.left < rect.left + rect.w 
                    && pos.top < rect.top + rect.h
        }
    })();
    

    var line = function(ctx1, ctx2, that) {
        var canvas1 = ctx1.canvas,
            canvas2 = ctx2.canvas;
        return {
            ing: function(start, end) {
                ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
                ctx2.beginPath();
                ctx2.moveTo(start.left, start.top);
                ctx2.lineTo(end.left, end.top);
                ctx2.stroke();
            },
            end: function(start, end) {
                ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
                var color = ctx1.strokeStyle;
                var lineWidth = ctx1.lineWidth;
                var draw = function() {
                    ctx1.save();
                    ctx1.strokeStyle = color;
                    ctx1.lineWidth = lineWidth;
                    ctx1.beginPath();
                    ctx1.moveTo(start.left, start.top);
                    ctx1.lineTo(end.left, end.top);
                    ctx1.stroke();
                    ctx1.restore();
                }
                draw();
                that.stack.push(draw);
                that.rstack = [];
            }
        }
    };

    var curve = function(ctx1, ctx2, that) {
      

        var canvas1 = ctx1.canvas,
            canvas2 = ctx2.canvas;

        var points = [];

        return {
            start: function(position) {
                let pat = that.palette['curve'];
                var color = pat.color;
                var width = pat.width;
                ctx2.strokeStyle = color;
                ctx2.lineWidth = width;
                

                ctx2.beginPath();
                ctx2.lineJoin = 'round';
                ctx2.moveTo(position.left, position.top);
                points.push(position);
            },

            ing: function(start, end) {
                ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
                ctx2.lineTo(end.left, end.top);
                points.push(end);
                ctx2.stroke();
            },

            end: function() {

                
                let pat = that.palette['curve'];
                var color = pat.color;
                var width = pat.width;
                
                let o = {points: points.splice(0), color: color, width: width}
                this.toDraw(o)

                that.listens.forEach((h) => {
                    h('curve', o )
                })

                points = [];
            },

            toDraw: function(o) {
                let _points  = o.points;
                
                var color = o.color;
                var width = o.width;
                ctx2.strokeStyle = color;
                ctx2.lineWidth = width;

                ctx2.clearRect(0, 0, canvas2.width, canvas2.height);

                //回退或前进功能中重绘时会重新读取该数组，而points是动态变化的，所以拷贝一份出来

                var stackPoints = _points.slice(0);
                
                
                var draw = function() {
                    ctx1.save();
                    ctx1.strokeStyle = color;
                    ctx1.lineWidth = width;
                    ctx1.beginPath();
                    $.each(stackPoints, function(i, p) {
                        if (i === 0) {
                            ctx1.moveTo(p.left, p.top);
                        } else {
                            ctx1.lineTo(p.left, p.top);
                        }
                    });
                    ctx1.stroke();
                    ctx1.restore();
                }
                draw();
                that.stack.push(draw);
                that.rstack = [];
                _points = [];
            },
        };
    };

    var text = function(ctx1, ctx2, that){

        var inputs = [];

        var fa = `PingFang SC, Hiragino Sans GB, Microsoft YaHei, WenQuanYi Micro Hei, sans-serif`;
        
        function draw(texts){
            let text = texts.text
            let color = texts.color
            let font = texts.font
            let pos = texts.position

            ctx1.save();
            ctx1.fillStyle = color;
            ctx1.font = font;
            ctx1.textBaseline = 'top';
            
            ctx1.fillText(
                text, 
                pos.x ,
                pos.y
               
            );
        }
        
        function input(position){

            let pat = that.palette['text'];
            var color = pat.color;
            var width = pat.width;
           

            var lw   = width;
            
            var top_offset = 6;
            var fs = lw*10;
            var lh =fs + top_offset * 2;
            var w = fs*10

            inputs.forEach($in => {
                if($.trim($in.val()) != ''){
                    ctx1.save();
                    ctx1.fillStyle = $in.css('color');
                    
                    let font =     $in.css('font-style') + ' ' +
                                    $in.css('font-variant') + ' '+  
                                    $in.css('font-weight') + ' '+  
                                    $in.css('font-size') + ' '+  
                                    $in.css('font-family');

                    let pos = {x: util.pxValue($in.css('left')), y: util.pxValue($in.css('top'))  }
                    let texts = {text: $in.val(), font: font, position: pos, color: color}
   
                    draw(texts)

                    that.listens.forEach((h) => {
                        h('text', texts)
                    })    

                }
                $in.remove();
                inputs = [];
            });

            if(position){

            var font        = `normal normal normal ${fs}px ${fa}`;
            let $input      = $(`<input class="addinput form-control" 
                            style="color:${color}; font: ${font}; width: ${w}px; line-height:${lh}px; position: absolute; 
                            top: ${position.top}px; left: ${position.left}px; background: #eee; " 
                            size="1024"
                            placeholder="Input..."/>`);
                            
                    that.parent.append($input);
                    inputs.push($input)

                    setTimeout(()=>{
                        $input.focus()
                    }, 400)
            }
                    
        }

        return {
            start: function(position) {
                input(position)
            },

            ing: function(start, end) {
            },

            end: function(start, end) {
                input(end)
            },

            done: function(){
                input()
            },

            toDraw: function(texts){
                draw(texts)
            }
        }
    }

    var section = function(ctx1, ctx2, that){
        var color = "#b6b6b6";
        var lineWidth = 1;

        var last = {left:0, top:0, w:0, h:0};

        var canvas1 = ctx1.canvas,
        canvas2 = ctx2.canvas;

           ctx1.lineWidth = lineWidth;
           ctx1.strokeStyle = color;

        var doing = false;
        var draging = false;
        var buf;

        var rectOffsetLeft = 0;
        var rectOffsetTop = 0;

        var toRect = function(start , end){
          
            let left = start.left < end.left ? start.left  : end.left
            let top = start.top < end.top ? start.top : end.top;

            let w = Math.abs(end.left - start.left)
            let h = Math.abs(end.top - start.top)

            return {left:left, top:top, w: w, h: h}
        }

        var draw = function(rect) {
            if(0 < rect.h){
              last = rect;
              buf = ctx1.getImageData(last.left, last.top, last.w, last.h)
            }
        }

        var move = function(position){
            ctx1.clearRect(last.left, last.top, last.w, last.h)
            ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
            if(buf){
                ctx1.putImageData(buf,  position.left, position.top);
                buf = null;
            }
        }

        var remove= function(){
            
            ctx1.clearRect(last.left-lineWidth, last.top-lineWidth, 
                Number(last.w) + Number(lineWidth) * 2,
                Number(last.h) + Number(lineWidth) * 2)
            
            ctx2.clearRect(0, 0, canvas2.width, canvas2.height);

        }

        return {
            start: function(position) {
                if( doing ){
                    if(util.inRect(position, last) ){
                        draging = true; 
                                              
            rectOffsetLeft = position.left - last.left;
            rectOffsetTop = position.top - last.top;
            

                        ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
                   
                    if(buf){
                        ctx2.putImageData(buf,  position.left-rectOffsetLeft, position.top-rectOffsetTop);
                    }

                    }else{
                        ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
                    }
                }
            },

            ing: function(start, end) {
                if(draging){
                    ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
                    if(buf){
                        ctx2.putImageData(buf,  end.left-rectOffsetLeft, end.top-rectOffsetTop);
                        ctx2.strokeRect(end.left-rectOffsetLeft, end.top-rectOffsetTop, last.w, last.h);
                    }
                }else{
                    ctx2.lineWidth = 1;
                    ctx2.strokeStyle = color;
                    ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
                    ctx2.strokeRect(start.left, start.top, end.left - start.left, end.top - start.top);
                    
                    doing = true;
                }
            },

            end: function(start, end) {
                if(draging){

                    if(buf){
          
            let left = end.left-rectOffsetLeft;
            let top =  end.top-rectOffsetTop
                        
                        move({ left,  top})
                        
                        that.listens.forEach((h) => {
                            h('move', {left, top} )
                        })

                    }
                
                    draging = false;
                    doing = false;
                }else{
                    let rect = toRect(start, end)
                    draw(rect);
                        
                that.listens.forEach((h) => {
                    h('section', last )
                })
            }
                
            },

            done: function(){
                 
            },

            toDraw: function(section){
                draw(section)
            },

            toMove: function(pos){
                move(pos)
            },

            toRemove(){
                remove()
            },

            onKeyDown: function(event){
                if(event.keyCode == 46){
                    remove()
                    that.listens.forEach((h) => {
                        h('remove', true)
                    })
                    doing = false;
                }
            }
             
        }
    }

    var round;
    (function() {
        var len = util.len;
        round = function(ctx1, ctx2, that) {
            var canvas1 = ctx1.canvas,
                canvas2 = ctx2.canvas;
            return {
                ing: function(start, end) {
                    ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
                    ctx2.beginPath();
                    ctx2.arc(start.left + (end.left - start.left) / 2, start.top + (end.top - start.top) / 2, len(start, end) / 2, 0, 2 * Math.PI);
                    ctx2.stroke();
                },
                end: function(start, end) {
                    ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
                    var color = ctx1.strokeStyle;
                    var lineWidth = ctx1.lineWidth;
                    var draw = function() {
                        ctx1.save();
                        ctx1.lineWidth = lineWidth;
                        ctx1.strokeStyle = color;
                        ctx1.beginPath();
                        ctx1.arc(start.left + (end.left - start.left) / 2, start.top + (end.top - start.top) / 2, len(start, end) / 2, 0, 2 * Math.PI);
                        ctx1.stroke();
                        ctx1.restore();
                    }
                    draw();
                    that.stack.push(draw);
                    that.rstack = [];
                }
            }
        }
    })();

    var rect = function(ctx1, ctx2, that) {
        var canvas1 = ctx1.canvas,
            canvas2 = ctx2.canvas;
        return {
            ing: function(start, end) {
                ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
                ctx2.strokeRect(start.left, start.top, end.left - start.left, end.top - start.top);
            },
            end: function(start, end) {
                ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
                var color = ctx1.strokeStyle;
                var lineWidth = ctx1.lineWidth;
                var draw = function() {
                    ctx1.save();
                    ctx1.lineWidth = lineWidth;
                    ctx1.strokeStyle = color;
                    ctx1.strokeRect(start.left, start.top, end.left - start.left, end.top - start.top);
                    ctx1.restore();
                }
                draw();
                that.stack.push(draw);
                that.rstack = [];
            }
        }
    };

    var ease = function(ctx1, ctx2, that) {
        var canvas1 = ctx1.canvas,
            canvas2 = ctx2.canvas,
            easeFn = [];
        return {
            ing: function(start, end) {

                this.toDraw(end)

                that.listens.forEach((h) => {
                    h('erase', end)
                })
            },

            end: function(start, end) {
                that.ctx2.clearRect(0, 0, that.canvas2.width, that.canvas2.height);
                var stackFn = easeFn.slice(0);
                that.stack.push(function() {
                    $.each(stackFn, function() {
                        this();
                    });
                });
                that.rstack = [];
                that.easeFn = [];
            },

            toDraw: function(end) {
                function c2() {
                    ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
                    ctx2.beginPath();
                    ctx2.arc(Math.floor(end.left), Math.floor(end.top), 10, 0, 2 * Math.PI);
                    ctx2.stroke();
                }
                c2 = util.throttle(c2, 140);
                c2();
                var draw = function() {
                    ctx1.globalCompositeOperation = "destination-out"; //鼠标覆盖区域不显示
                    ctx1.beginPath();
                    ctx1.arc(Math.floor(end.left), Math.floor(end.top), 10, 0, 2 * Math.PI, true);
                    ctx1.closePath();
                    ctx1.fill();
                    ctx1.globalCompositeOperation = "source-over";
                }
                draw();
                easeFn.push(draw);
            }
        };
    }
    var arrow = function(ctx1, ctx2, that) {
        var arrow = function(ctx2, start, end) {
            ctx2.save()
            ctx2.beginPath()
            ctx2.moveTo(start.left, start.top)
            ctx2.lineTo(end.left, end.top)
            ctx2.fillStyle = ctx2.strokeStyle

            var atan = Math.atan((end.top - start.top) / (end.left - start.left));
            var rotate = Math.PI / 2 + atan
            if (end.left < start.left) {
                rotate = rotate - Math.PI
            }

            ctx2.translate(end.left, end.top)
            ctx2.rotate(rotate)

            var angle = 30 / 180 * Math.PI / 2
            var width = 15 * Math.tan(angle)
            ctx2.moveTo(0, 0)
            ctx2.lineTo(width, 15)
            ctx2.lineTo(-width, 15)

            ctx2.stroke()
            ctx2.fill()
            ctx2.restore()
        }
        return {
            start: function(position) {

            },
            ing: function(start, end) {
                ctx2.clearRect(0, 0, ctx2.canvas.width, ctx2.canvas.height)
                arrow(ctx2, start, end)
            },
            end: function(start, end) {
                ctx2.clearRect(0, 0, ctx2.canvas.width, ctx2.canvas.height)
                var color = that.color
                var draw = function() {
                    ctx1.strokeStyle = color
                    arrow(ctx1, start, end)
                }
                draw()
                that.stack.push(draw)
                that.rstack = [];
            }
        }
    };


    var DrawBoard;
    (function() {
        var ident = function() {
            return false;
        };
        /**
         * 为canvas添加绘图功能
         * @param canvas1和canvas2是两个重叠的canvas标签 canvas2在canvas1上面
         * 对于ie，不支持canvas，canvas1和canvas2是excanvas初始化的canvas对象
         */
        DrawBoard = function(option) {
            console.info("DrawBoard....")
            var that = this;

            this.option = option || {};
            this.type = option.type || 'rect';
            this.lineWidth = option.lineWidth || 1;
            this.color = option.color || 'rgb(0, 0, 0)'
            this.stack = [];
            this.rstack = [];
            this.listens = [];
            this.draw = {};
            this.palette = {}
            this.parent = $(option.parent)
            var canvas1 = this.canvas1 = document.createElement('canvas'),
                $canvas1 = this.$canvas1 = $(canvas1),
                canvas2 = this.canvas2 = document.createElement('canvas'),
                $canvas2 = this.$canvas2 = $(canvas2),
                $canvases = $canvas1.add($canvas2),
                $con = $('<div></div>').append($canvas1, $canvas2);
            $con.css({ width: option.width, height: option.height, position: 'relative' });
            canvas1.width = canvas2.width = option.width;
            canvas1.height = canvas2.height = option.height;
            $canvases.css({ position: 'absolute', left: 0, top: 0 });
            $con.appendTo(this.parent);

            if (!canvas1.getContext) {
                if (window.G_vmlCanvasManager) {
                    canvas1 = window.G_vmlCanvasManager.initElement(canvas1);
                    canvas2 = window.G_vmlCanvasManager.initElement(canvas2);
                } else {
                    alert('对不起，您的浏览器不支持canvas!');
                }
            }
            var ctx1 = this.ctx1 = canvas1.getContext('2d');
            var ctx2 = this.ctx2 = canvas2.getContext('2d');
            var option = option || {
                clearBt: null, //清除按钮
                saveBt: null //保存按钮
            };
            ctx1.save();
            ctx2.strokeStyle = this.color;
            ctx1.strokeStyle = this.color;
            ctx2.lineWidth = this.lineWidth;
            ctx1.lineWidth = this.lineWidth;
            var mouse = {};
            $(canvas2).mousemove(function(e) {
                mouse = getMouseOffset($(canvas2).get(0), e);
            });

            this.palette.curve = {width:1, color:'#000'}
            this.palette.text = {width:1, color:'#000'}

            var draw = this.draw;
            draw.rect = rect(ctx1, ctx2, that);
            draw.round = round(ctx1, ctx2, that);
            draw.line = line(ctx1, ctx2, that);
            draw.ease = ease(ctx1, ctx2, that);
            draw.curve = curve(ctx1, ctx2, that);
            draw.text = text(ctx1, ctx2, that);
            draw.section = section(ctx1, ctx2, that);

            ctx1.canvas = canvas1;
            ctx2.canvas = canvas2;

            draw.arrow = arrow(ctx1, ctx2, that);

            drag(canvas2, {
                dragstart: function(position) {
                    that.refuseSelection();
                    (draw[that.type].start || $.noop)(position);
                },
                dragcontinue: function(position) {
                    that.refuseSelection();
                    (draw[that.type]["continue"] || $.noop)(position);
                },
                dragpause: function(position) {
                    that.allowSelection();
                    (draw[that.type].pause || $.noop)(position);
                },
                dragging: function(start, end) {
                    draw[that.type].ing(start, end);
                },
                dragend: function(start, end) {
                    draw[that.type].end(start, end);
                    that.allowSelection();
                }
            });

            $(document).keydown(function(event){
                draw[that.type].onKeyDown &&
                　draw[that.type].onKeyDown(event)
        　　　});
        };

        DrawBoard.prototype = {
            setDrawErase: function(point) {
                this.draw.ease.toDraw(point)
            },

            setDrawCurve: function(points) {
                this.draw.curve.toDraw(points)
            },

            setDrawText: function(text){
                this.draw.text.toDraw(text);
            },

            setDrawSection: function(section){
                this.draw.section.toDraw(section);
            },

            setDrawMove: function(pos){
                this.draw.section.toMove(pos);
            },

            setDrawRemove: function(pos){
                this.draw.section.toRemove(pos);
            },


            addListenner: function(h) {
                this.listens.push(h);
            },

            //撤消
            back: function() {
                this.ctx1.clearRect(0, 0, this.canvas1.width, this.canvas1.height);
                var pop = this.stack.pop();
                if (pop) {
                    this.rstack.push(pop);
                }
                this.drawStack();
            },

            //重做
            forward: function() {
                this.ctx1.clearRect(0, 0, this.canvas1.width, this.canvas1.height);
                var pop = this.rstack.pop();
                if (pop) {
                    this.stack.push(pop);
                }
                this.drawStack();
            },

            //重绘
            drawStack: function() {
                this.stack.forEach(s => {
                    s && s();
                })
            },

            setColor: function(type, color) {
                console.log('setColor', type, color)
                this.palette[type].color = color;
            },

            setLineWidth: function(type, lineWidth) {
                console.log('setWidth', type, lineWidth)
                this.palette[type].width = lineWidth;
            },

            setType: function(type){
                this.type = type;
                for(var k in this.draw){
                    this.draw[k].done && this.draw[k].done()
                }
                    
            },

            refuseSelection: function() {
                $('body').attr('unselectable', 'on').css({
                    '-webkit-user-select': 'none',
                    /* Chrome all / Safari all */
                    '-moz-user-select': 'none',
                    /* Firefox all */
                    '-ms-user-select': 'none',
                    /* IE 10+ */
                    /* No support for these yet, use at own risk */
                    '-o-user-select': 'none',
                    'user-select': 'none'
                }).bind('selectstart', ident);
                this.clearSelection();
            },
            allowSelection: function() {
                $('body').attr('unselectable', 'off').css({
                    '-webkit-user-select': 'auto',
                    /* Chrome all / Safari all */
                    '-moz-user-select': 'auto',
                    /* Firefox all */
                    '-ms-user-select': 'auto',
                    /* IE 10+ */
                    /* No support for these yet, use at own risk */
                    '-o-user-select': 'auto',
                    'user-select': 'auto'
                }).unbind('selectstart', ident);
            },
            clearSelection: function() {
                if (document.selection) {
                    document.selection.empty();
                } else if (window.getSelection) {
                    window.getSelection().removeAllRanges();
                }
            },
            clear: function() {
                var that = this,
                    canvas2 = this.canvas2;
                var draw = function() {
                    that.ctx1.clearRect(0, 0, canvas2.width, canvas2.height);
                    that.rstack = [];
                }
                draw();
                this.stack.push(draw);
            },
            save: function(el) {
                console.log('save ...')
            },

            setEnable(enable) {
                DRAW_BOARD_ENABLE = enable
            }
        }
    })();
    window.DrawBoard = DrawBoard;
})(window, jQuery);
